package lib.gen;

/**
 * Handles many functions that involves calculating distributions
 * @author dolphinigle
 */
public abstract class Distribution {

  /**
   * Generates an exponential-based distribution of longs from a given range
   * <br/><br/>
   * It generates a sequence such that :
   * <ul>
   * <li>The first element is equal to lower_bound</li>
   * <li>The last element is equal to upper_bound</li>
   * <li>There exist a double D such that for all i,
   * ret[i] = Math.ceil(ret[i-1] * D)</li>
   * </ul>
   * Note that this automatically implies the list is sorted in ascending
   * order
   *
   * @param n number of longs in the generated distribution
   * @param lower_bound lower bound of the numbers in distribution
   * @param upper_bound upper bound of the numbers in distribution
   * @return a sequence of longs containing n elements
   */
  public static long[] generateExponentialDistributionLong(int n,
          long lower_bound,
          long upper_bound) {

    assert (n >= 2);
    assert (lower_bound <= upper_bound);

    double lbound = 1.0;
    double ubound = (double)upper_bound / (double)lower_bound;

    long[] ret = null;

    for (int it = 0; it < 60; ++it) {
      double mid = (lbound + ubound) / 2.0;

      ret = new long[n];

      ret[0] = lower_bound;
      boolean larger = false;
      for (int i = 1; i < n; ++i) {
        ret[i] = (long)Math.round(Math.ceil((double)ret[i-1] * mid));
        if (ret[i] > upper_bound) {
          larger = true;
          break;
        }
      }
      if (larger) {
        ubound = mid;
        continue;
      }
      if (ret[n-1] < upper_bound) {
        lbound = mid;
        continue;
      }
      return ret;
    }

    for (int i = 0; i < ret.length; ++i) {
      if (ret[i] > upper_bound) ret[i] = upper_bound;
    }
    return ret;
  }

  /**
   * Generates an exponential-based distribution of integers from a given range
   * <br/><br/>
   * It generates a sequence such that :
   * <ul>
   * <li>The first element is equal to lower_bound</li>
   * <li>The last element is equal to upper_bound</li>
   * <li>There exist a double D such that for all i,
   * ret[i] = Math.ceil(ret[i-1] * D)</li>
   * </ul>
   * Note that this automatically implies the list is sorted in ascending
   * order
   *
   * @param n number of integers in the generated distribution
   * @param lower_bound lower bound of the numbers in distribution
   * @param upper_bound upper bound of the numbers in distribution
   * @return a sequence of integers containing n elements
   */
  public static int[] generateExponentialDistributionInt(int n, int lower_bound,
          int upper_bound) {

    assert (n >= 2);
    assert (lower_bound <= upper_bound);

    long ds[] = Distribution.generateExponentialDistributionLong(
            n, lower_bound, upper_bound);
    int ret[] = new int[n];
    for (int i = 0; i < n; ++i) ret[i] = (int) ds[i];
    return ret;


  }

}
